# -*- coding: utf-8 -*-

import os
import io
import base64
import re
import tempfile
from contextlib import closing
import subprocess
import logging

from odoo import models, fields, api, tools, _
from odoo.tools import config
from odoo.tools.misc import find_in_path
from odoo.sql_db import TestCursor


_logger = logging.getLogger(__name__)


def _get_wkhtmltoimage_bin():
    return find_in_path('wkhtmltoimage')


wkhtmltoimage_state = 'install'
try:
    process = subprocess.Popen(
        [_get_wkhtmltoimage_bin(), '--version'], stdout=subprocess.PIPE, stderr=subprocess.PIPE
    )
except (OSError, IOError):
    _logger.info('You need Wkhtmltoimage to print a image version of the reports.')
else:
    _logger.info('Will use the Wkhtmltoimage binary at %s' % _get_wkhtmltoimage_bin())
    out, err = process.communicate()
    match = re.search(b'([0-9.]+)', out)
    if match:
        wkhtmltoimage_state = 'ok'

        if config['workers'] == 1:
            _logger.info('You need to start Odoo with at least two workers to print a pdf version of the reports.')
            wkhtmltoimage_state = 'workers'
    else:
        _logger.info('Wkhtmltoimage seems to be broken.')
        wkhtmltoimage_state = 'broken'


class IrActionsReport(models.Model):
    _inherit = 'ir.actions.report'

    report_type = fields.Selection([
        ('qweb-html', 'HTML'),
        ('qweb-pdf', 'PDF'),
        ('qweb-text', 'Text'),
        ('qweb-jpg', 'JPEG'),
    ], required=True, default='qweb-pdf',
        help='The type of the report that will be rendered, each one having its own'
             ' rendering method. HTML means the report will be opened directly in your'
             ' browser PDF means the report will be rendered using Wkhtmltopdf and'
             ' downloaded by the user.')

    @api.multi
    def render_qweb_jpg(self, res_ids=None, data=None):
        if not data:
            data = {}

        # remove editor feature in pdf generation
        data.update(enable_editor=False)

        if tools.config['test_enable']:
            return self.render_qweb_html(res_ids, data=data)

        context = dict(self.env.context)
        if not config['test_enable']:
            context['commit_assetsbundle'] = True

        context['debug'] = False

        if isinstance(self.env.cr, TestCursor):
            return self.with_context(context).render_qweb_html(res_ids, data=data)[0]

        save_in_attachment = {}
        if res_ids:
            Model = self.env[self.model]
            record_ids = Model.browse(res_ids)
            wk_record_ids = Model
            if self.attachment:
                for record_id in record_ids:
                    attachment_id = self.retrieve_attachment(record_id)
                    if attachment_id:
                        save_in_attachment[record_id.id] = attachment_id
                    if not self.attachment_use or not attachment_id:
                        wk_record_ids += record_id
            else:
                wk_record_ids = record_ids
            res_ids = wk_record_ids.ids

        if save_in_attachment and not res_ids:
            _logger.info('The JPG report has been generated from attachments.')
            return self._post_jpg(save_in_attachment), 'jpg'

        if self.get_wkhtmltopdf_state() == 'install':
            raise UserError(_("Unable to find Wkhtmltoimage on this system. The JPG can not be created."))

        html = self.with_context(context).render_qweb_html(res_ids, data=data)[0]

        html = html.decode('utf-8')

        bodies, html_ids, header, footer, specific_paperformat_args = self.with_context(context)._prepare_html(html)

        if self.attachment and set(res_ids) != set(html_ids):
            raise UserError(_("The report's template '%s' is wrong, please contact your administrator. \n\n"
                              "Can not separate file to save as attachment because the report's template "
                              "does not contains the attributes 'data-oe-model' "
                              "and 'data-oe-id' on the div with 'article' classname.") % self.name)

        jpg_content = self._run_wkhtmltoimage(bodies)
        if res_ids:
            _logger.info('The JPG report has been generated for records %s.' % (str(res_ids)))
            return self._post_jpg(save_in_attachment, jpg_content=jpg_content, res_ids=html_ids), 'jpg'
        return jpg_content, 'jpg'

    @api.model
    def _run_wkhtmltoimage(self, bodies):
        temporary_files = []
        paths = []
        for i, body in enumerate(bodies):
            prefix = '%s%d.' % ('report.body.tmp.', i)
            body_file_fd, body_file_path = tempfile.mkstemp(suffix='.html', prefix=prefix)
            with closing(os.fdopen(body_file_fd, 'wb')) as body_file:
                body_file.write(body)
            paths.append(body_file_path)
            temporary_files.append(body_file_path)

        jpg_report_fd, jpg_report_path = tempfile.mkstemp(suffix='.jpg', prefix='report.tmp.')
        os.close(jpg_report_fd)
        temporary_files.append(jpg_report_path)

        command_args = self._build_wkhtmltoimage_args()

        try:
            wkhtmltoimage = [_get_wkhtmltoimage_bin()] + command_args + paths + [jpg_report_path]
            _logger.info('Command of wkhtmltoimage: %s', wkhtmltoimage)
            process = subprocess.Popen(wkhtmltoimage, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
            out, err = process.communicate()

            if process.returncode not in [0, 1]:
                if process.returncode == -11:
                    message = _(
                        'Wkhtmltoimage failed (error code: %s). '
                        'Memory limit too low or maximum file number of subprocess reached. Message : %s')
                else:
                    message = _('Wkhtmltoimage failed (error code: %s). Message: %s')
                raise UserError(message % (str(process.returncode), err[-1000:]))
            else:
                if err:
                    _logger.warning('wkhtmltoimage: %s' % err)
        except:
            raise

        with open(jpg_report_path, 'rb') as jpg_document:
            jpg_content = jpg_document.read()

        for temporary_file in temporary_files:
            try:
                os.unlink(temporary_file)
            except (OSError, IOError):
                _logger.error('Error when trying to remove file %s' % temporary_file)

        return jpg_content

    @api.model
    def _build_wkhtmltoimage_args(self):
        command_args = ['--disable-smart-width']
        page_width = self.env['ir.config_parameter'].sudo().get_param('exhibition.label_card_width') or 574
        page_width = str(page_width)
        command_args.extend(['--width', page_width])
        return command_args

    @api.model
    def get_wkhtmltoimage_state(self):
        '''Get the current state of wkhtmltoimage: install, ok, upgrade, workers or broken.
        * install: Starting state.
        * upgrade: The binary is an older version (< 0.12.0).  -- not use as don't know the minimum version working properly
        * ok: A binary was found with a recent version (>= 0.12.0).
        * workers: Not enough workers found to perform the pdf rendering process (< 2 workers).
        * broken: A binary was found but not responding.

        :return: wkhtmltoimage_state
        '''
        return wkhtmltoimage_state

    @api.multi
    def _post_jpg(self, save_in_attachment, jpg_content=None, res_ids=None):
        if jpg_content:
            jpg_content_stream = io.BytesIO(jpg_content)
            for rec in self.env[self.model].browse([res_id for res_id in res_ids if res_id]):
                self.postprocess_pdf_report(rec, jpg_content_stream)
                _logger.info('save JPG to attachment: %s %s', self.model, rec.id)
            return jpg_content
        attachment_content = list(save_in_attachment.values())[0].datas
        return base64.b64decode(attachment_content)



